// Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or  modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.

var eb = require("../eventBus");
var Handlebones = require("handlebones");
var PostMessageUtils = require("../util/postMessageUtils");
var preloadHelper = require("../util/preloadHelper");
var template = require("../templates/vote-view.handlebars");
var Utils = require("../util/utils");
var Strings = require("../strings");
var $ = require("jquery");
var _ = require("lodash");

var iOS = Utils.isIos();

function getOfficialTranslations(translations) {
  return (translations || []).filter(function (t) {
    return t.src > 0;
  });
}
function getMatchingOfficialTranslation(translations) {
  return getOfficialTranslations(translations).filter(function (t) {
    return Utils.matchesUiLang(t.lang);
  })[0];
}

module.exports = Handlebones.ModelView.extend({
  name: "vote-view",
  template: template,
  events: {
    "click #agreeButton": "participantAgreed",
    "click #disagreeButton": "participantDisagreed",
    "click #passButton": "participantPassed",
    "click #subscribeBtn": "subscribeBtn",
    "submit #subscribeEmailForm": "subscribeBtn",

    "click #spamToggle": "spamToggle",
    "click #otToggle": "otToggle",
    "click #importantToggle": "importantToggle",
    "click #modSubmit": "participantModerated",

    "click #showTranslationButtonVoteView": "showTranslationClicked",
    "click #hideTranslationButtonVoteView": "hideTranslationClicked"
  },
  context: function () {
    var ctx = Handlebones.ModelView.prototype.context.apply(this, arguments);
    ctx.iOS = iOS;
    ctx.customStyles = "";
    // if (ctx.txt && ctx.txt.length < 30) {
    //   ctx.customStyles += "text-align: center; ";
    // ctx.customStyles += "padding-top: 39px; ";
    //   ctx.customStyles += "font-size: 22px; ";
    // }
    ctx.email = userObject.email;
    ctx.subscribed = this.isSubscribed();
    if (ctx.created) {
      ctx.createdString = new Date(ctx.created * 1).toString().match(/(.*?) [0-9]+:/)[1];
    }
    ctx.s = Strings;

    var btnBg = preload.conversation.style_btn;
    if (btnBg) {
      ctx.customBtnStyles = "background-color: " + btnBg + ";";
    }
    if (Strings.direction == "rtl") {
      ctx.pMarginStyle = "margin-right: 55px;";
      ctx.floatStyle = "float:left;"; // This is a hack to fix the bug in chrome, not supporting float:inline-start and end
    } else {
      ctx.pMarginStyle = "margin-left: 55px;";
      ctx.floatStyle = "float:right;";
    }

    var social = ctx.social;
    var socialCtx = {
      name: Strings.anonPerson,
      img: Utils.getAnonPicUrl(),
      link: "",
      anon: true
    };
    if (social) {
      var hasX = social.x_name;
      if (hasX) {
        socialCtx = {
          name: social.x_name,
          img: social.x_profile_image_url
        };
      }
    }
    ctx.showImportantCheckbox = preload.conversation.importance_enabled;
    ctx.social = socialCtx;
    ctx.noModSet = !ctx.spamOn && !ctx.otOn && !ctx.importantOn;
    ctx.canSubscribe = !!preload.firstPtpt || this.votesByMe.size() > 0;
    ctx.canSubscribe = ctx.canSubscribe && Utils.userCanSeeSubscribePrompt();

    if (ctx.showTranslation && ctx.translations && ctx.translations.length) {
      ctx.translationTxt = ctx.translations[0].txt;
      ctx.translationLang = ctx.translations[0].lang;
      ctx.translationSrc = ctx.translations[0].src;
    }
    if (
      !ctx.showTranslation &&
      ctx.lang &&
      !Utils.matchesUiLang(ctx.lang) &&
      ctx.translations &&
      ctx.translations.length
    ) {
      ctx.showShowTranslationButton = true;
    }
    if (
      ctx.showTranslation &&
      ctx.translationTxt &&
      ctx.lang &&
      !Utils.matchesUiLang(ctx.lang) &&
      ctx.translations &&
      ctx.translations.length
    ) {
      ctx.showHideTranslationButton = true;
    }

    var officialTranslation = null;
    if (ctx.showOfficialTranslation) {
      // && ctx.translationSrc !== -1) {
      officialTranslation = getMatchingOfficialTranslation(ctx.translations);
      ctx.txt = officialTranslation.txt;
      ctx.lang = officialTranslation.lang;
    }

    if (officialTranslation) {
      ctx.isUnofficialTranslation = false;
      ctx.showTranslationButton = Strings.showTranslationButton;
    } else if (ctx.translations && ctx.translations.length > 0) {
      ctx.isUnofficialTranslation = true;
      ctx.showTranslationButton = Strings.showTranslationButton;
      ctx.thirdPartyTranslationDisclaimer = Strings.thirdPartyTranslationDisclaimer;
    }
    // if comment has correct language don't show translation or buttons
    if ((ctx.showTranslation || officialTranslation) && ctx.lang && Utils.matchesUiLang(ctx.lang)) {
      ctx.showTranslation = false;
      delete ctx.translationTxt;
      delete ctx.translationLang;
      delete ctx.translationSrc;
      ctx.showShowTranslationButton = false;
      ctx.showHideTranslationButton = false;
    }

    var remaining = ctx.remaining;
    if (remaining > 100) {
      remaining = "100+";
    }
    ctx.remainingString = Strings.comments_remaining.replace("{{num_comments}}", remaining);
    ctx.remainingStringScreenReader = Strings.comments_remaining2.replace("{{num_comments}}", remaining);
    return ctx;
  },

  showTranslationClicked: function (e) {
    e.preventDefault();
    this.model.set({
      showTranslation: true
    });
    this.serverClient.put_participants_extended({
      show_translation_activated: true
    });
  },

  hideTranslationClicked: function (e) {
    e.preventDefault();
    this.model.set({
      showTranslation: false
    });
    this.serverClient.put_participants_extended({
      show_translation_activated: false
    });
  },

  spamToggle: function () {
    this.model.set({
      spamOn: !this.model.get("spamOn"),
      otOn: false,
      importantOn: false
    });
  },
  otToggle: function () {
    this.model.set({
      spamOn: false,
      otOn: !this.model.get("otOn"),
      importantOn: false
    });
  },
  importantToggle: function () {
    this.model.set({
      spamOn: false,
      otOn: false,
      importantOn: !this.model.get("importantOn")
    });
  },

  animateOut: function () {
    // Animating opacity directly instead of jQuery's fadeOut because we don't want display:none at the end.
    this.$el.children().children().animate(
      {
        opacity: 0
      },
      200
    );
  },
  animateIn: function () {
    this.$el.children().children().animate(
      {
        opacity: 1
      },
      200
    );
  },
  showMod: function () {
    this.model.set("shouldMod", true);
  },
  initialize: function (options) {
    Handlebones.ModelView.prototype.initialize.apply(this, arguments);
    eb.on(eb.exitConv, cleanup);

    function cleanup() {
      eb.off(eb.exitConv, cleanup);
    }
    var serverClient = (this.serverClient = options.serverClient);
    var votesByMe = (this.votesByMe = options.votesByMe);
    var votesByMeFetched = $.Deferred();
    votesByMeFetched.then(function () {
      if (votesByMe.size() > 0) {
        eb.trigger(eb.interacted);
      }
    });
    this.conversationModel = options.conversationModel;

    var is_public = options.is_public;
    var conversation_id = (this.conversation_id = options.conversation_id);
    this.pid = options.pid;
    this.isSubscribed = options.isSubscribed;

    if (Utils.isDemoMode()) {
      votesByMeFetched.resolve();
    }
    votesByMe.on("sync", votesByMeFetched.resolve);

    var that = this;
    var waitingForComments = true;
    var commentPollInterval = 5 * 1000;

    function pollForComments(optionalPromiseForPreExisingNextCommentCall) {
      if (waitingForComments && !Utils.isHidden()) {
        getNextAndShow(optionalPromiseForPreExisingNextCommentCall);
      } else {
        // try to try again later
        setTimeout(pollForComments, commentPollInterval);
      }
    }

    function showComment(model) {
      var savedState = {
        showTranslation: that.model.get("showTranslation"),
        empty: false,
        shouldMod: false
      };

      var t = getMatchingOfficialTranslation(model.translations);
      if (t) {
        savedState.showOfficialTranslation = true;
      }

      that.model.clear({
        silent: true
      });
      that.model.set(_.extend(savedState, model));
      that.render();
      that.trigger("showComment");
      waitingForComments = false;
    }

    function getNextAndShow(optionalPromiseForPreExisingNextCommentCall) {
      var params = {};
      if (that.model && that.model.get("tid")) {
        params.notTid = that.model.get("tid");
      }
      var promise = optionalPromiseForPreExisingNextCommentCall || serverClient.getNextComment(params);
      promise.then(
        function (c) {
          var emailEl = that.$(".email");
          var email = emailEl.val() || emailEl.is(":focus");
          if (!email) {
            // Don't clobber view if user is writing an email
            if (!that.conversationModel.get("is_active")) {
              showClosedConversationNotice();
            } else if (c && c.txt) {
              showComment(c);
            } else {
              showEmpty();
            }
          }
          setTimeout(pollForComments, commentPollInterval);
        },
        function (err) {
          setTimeout(pollForComments, commentPollInterval);
        }
      );
    }

    function onFail(err) {
      this.animateIn();
      console.error(err);

      // Parse error from JSON response or fall back to plain text
      var errorCode = null;
      if (err && err.responseJSON && err.responseJSON.error) {
        errorCode = err.responseJSON.error;
      } else if (err && err.responseText) {
        errorCode = err.responseText;
      }

      if (errorCode === "polis_err_conversation_is_closed") {
        alert(Strings.convIsClosed);
      } else if (errorCode === "polis_err_post_votes_social_needed") {
        that.model.set({
          needSocial: true
        });
      } else if (errorCode === "polis_err_xid_required") {
        alert(Strings.xidRequired);
      } else if (errorCode === "polis_err_xid_not_allowed") {
        alert(Strings.xidRequired);
      } else {
        alert(Strings.voteErrorGeneric);
      }
    }

    function onVote(result) {
      eb.trigger(eb.vote, this.mostRecentVoteType);
      eb.trigger(eb.interacted);
      setTimeout(PostMessageUtils.postVoteEvent);

      if (result.shouldMod) {
        this.showMod(); // result of mod should include a nextComment property.
      } else if (result.nextComment) {
        showComment(result.nextComment);
      } else {
        showEmpty();
      }
      this.animateIn();

      // this.model.set({
      //   userHasVotedThisSession: true,
      // });
      this.$("#hereIsNextStatementLabel").show();

      // Fix for stuck hover effects for touch events.
      // Remove when this is fix is accepted
      // https://github.com/twbs/bootstrap/issues/12832
      this.$(".btn-vote").blur();
    }

    function showClosedConversationNotice() {
      that.model.clear({
        silent: true
      });
      that.model.set({
        empty: true,
        txt1: Strings.convIsClosed,
        txt2: Strings.noMoreVotingAllowed
      });
      that.render();
    }

    function showEmpty() {
      $.when(preloadHelper.firstVotesByMePromise, preloadHelper.firstPtptPromise).then(
        _.defer(function () {
          var userHasVoted =
            !!votesByMe.size() ||
            (preload.firstVotesByMe && preload.firstVotesByMe.length) ||
            (preload.firstPtpt && preload.firstPtpt.vote_count > 0);

          waitingForComments = true;
          // pollForComments();
          var ucw = Utils.userCanWrite();
          var message1;
          var message2;
          if (userHasVoted) {
            message1 = Strings.noCommentsYouVotedOnAll;
            if (ucw) {
              message2 = Strings.noCommentsTryWritingOne;
            }
          } else {
            message1 = Strings.noCommentsYet;
            if (is_public) {
              message2 = Strings.noCommentsYetSoInvite;
            } else if (ucw) {
              message2 = Strings.noCommentsYetSoWrite;
            }
          }

          // TODO show some indication of whether they should wait around or not (how many active users there are, etc)
          that.model.clear({
            silent: true
          });
          that.model.set({
            empty: true,
            txt1: message1,
            txt2: message2
          });
          that.render();
        })
      );
    }

    this.onButtonClicked = function () {
      this.animateOut();
    };
    this.starBtn = function (e) {
      var starred = !that.model.get("starred");

      that.model.set("starred", starred);
      if (starred) {
        $("#starredLabel").fadeIn(200);
        setTimeout(function () {
          $("#starredLabel").fadeOut(600);
        }, 1500);
      }
    };
    this.subscribeBtn = function (e) {
      var that = this;
      var email = this.$(".email").val() || preload.firstUser.email;
      serverClient
        .convSub({
          type: 1, // 1 for email
          email: email,
          conversation_id: conversation_id
        })
        .then(
          function () {
            userObject.email = that.$(".email").val(); // TODO this is kinda crappy
            that.isSubscribed(1); // TODO this is totally crappy
            that.$(".email").val("");
            that.model.set("foo", Math.random()); // trigger render
          },
          function (err) {
            alert(Strings.notificationsSubscribeErrorAlert);
            console.error(err);
          }
        );
      return false;
    };

    // note: instead of -1/1/0, weight is now the boolean high_priority
    this.highPriority = function () {
      if ($("#weight_high").prop("checked")) {
        return true;
      }
      return false;
    };
    this.participantAgreed = function (e) {
      this.mostRecentVoteType = "agree";
      var tid = this.model.get("tid");
      var starred = this.model.get("starred");
      if (!starred) {
        starred = void 0; // don't bother sending up false, no need to put a vote value of 0 in the db.
      }
      this.wipVote = {
        vote: -1,
        conversation_id: conversation_id,
        high_priority: this.highPriority(),
        tid: tid
      };
      serverClient.addToVotesByMe(this.wipVote);
      this.onButtonClicked();
      serverClient.agree(tid, starred, this.wipVote.high_priority).then(onVote.bind(this), onFail.bind(this));
    };
    this.participantDisagreed = function () {
      this.mostRecentVoteType = "disagree";
      var tid = this.model.get("tid");
      var starred = this.model.get("starred");
      this.wipVote = {
        vote: 1,
        conversation_id: conversation_id,
        high_priority: this.highPriority(),
        tid: tid
      };
      serverClient.addToVotesByMe(this.wipVote);
      this.onButtonClicked();
      serverClient.disagree(tid, starred, this.wipVote.high_priority).then(onVote.bind(this), onFail.bind(this));
    };
    this.participantPassed = function () {
      this.mostRecentVoteType = "pass";
      var tid = this.model.get("tid");
      var starred = this.model.get("starred");
      this.wipVote = {
        vote: 0,
        conversation_id: conversation_id,
        high_priority: this.highPriority(), // TODO: specify in help text that this is for "important but unsure"
        tid: tid
      };
      serverClient.addToVotesByMe(this.wipVote);
      this.onButtonClicked();
      serverClient.pass(tid, starred, this.wipVote.high_priority).then(onVote.bind(this), onFail.bind(this));
    };

    this.participantModerated = function (e) {
      var tid = this.model.get("tid");
      serverClient
        .mod(tid, {
          spam: this.model.get("spamOn"),
          offtopic: this.model.get("otOn"),
          important: this.model.get("importantOn")
        })
        .then(onVote.bind(this), onFail.bind(this));
    };

    this.participantStarred = function () {
      var tid = this.model.get("tid");
      serverClient.addToVotesByMe({
        participantStarred: true,
        vote: -1,
        conversation_id: conversation_id,
        tid: tid
      });
      this.onButtonClicked();
      $.when(serverClient.star(tid), serverClient.agree(tid)).then(onVote.bind(this), onFail.bind(this));
    };
    this.participantTrashed = function () {
      var tid = this.model.get("tid");
      this.onButtonClicked();
      serverClient.trash(tid).then(onVote.bind(this), onFail.bind(this));
    };

    this.onAuthSuccess = function () {
      if (!this.wipVote) {
        alert(1);
        return;
      }
      var starred = this.model.get("starred");
      var high_priority = this.wipVote.high_priority;

      var tid = this.wipVote.tid;

      function reloadPage() {
        eb.trigger(eb.reload);
      }
      function onFailAfterAuth() {
        alert(2);
      }

      if (this.wipVote.vote === -1) {
        serverClient.agree(tid, starred, high_priority).then(reloadPage, onFailAfterAuth);
      } else if (this.wipVote.vote === 0) {
        serverClient.pass(tid, starred, high_priority).then(reloadPage, onFailAfterAuth);
      } else if (this.wipVote.vote === 1) {
        serverClient.disagree(tid, starred, high_priority).then(reloadPage, onFailAfterAuth);
      } else {
        alert(3);
      }
    };

    pollForComments(options.firstCommentPromise); // call immediately using a promise for the first comment (latency reduction hack)
    this.listenTo(this, "rendered", function () {
      this.$("#passButton").tooltip({
        title: "'No reaction', or 'I am unsure'",
        placement: "top",
        delay: {
          show: 500,
          hide: 0
        }
      });
      this.$("#trashButton").tooltip({
        title: "This comment is irrelevant and/or abusive",
        placement: "top",
        delay: {
          show: 500,
          hide: 0
        }
      });
    });
  }
});
